home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / doom / ldhe-src.0 / ldhe-src / dehacked / source / files.cc < prev    next >
C/C++ Source or Header  |  1995-05-31  |  49KB  |  1,754 lines

  1. // DeHackEd version 2.3
  2. // Written by Greg Lewis, gregl@umich.edu
  3. // If you release any versions of this code, please include
  4. // the author in the credits.  Give credit where credit is due!
  5.  
  6. #include <sys/param.h>
  7. #include <ctype.h>
  8. #include <errno.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11.  
  12. #include "dehacked.h"
  13. #include "files.h"
  14. #include "linux_text.h"
  15.  
  16.  
  17. // This is a fun function -- Basically, change the gender of all the data. ;-)
  18.  
  19. static int gender_table[NUMVERS] = {
  20.     LITTLE_ENDIAN,            // DOOM1_2
  21.     LITTLE_ENDIAN,            // DOOM1_6
  22.     LITTLE_ENDIAN,            // DOOM2_0
  23.     LITTLE_ENDIAN,            // DOOM1_9
  24.     LITTLE_ENDIAN,            // LNX_X18
  25.     LITTLE_ENDIAN,            // LNX_S18
  26.     BIG_ENDIAN,            // SGI_X18
  27. };
  28.  
  29. #define bytesex_row(data, fields, V)    \
  30.     if ( BYTE_ORDER != gender_table[V] ) { \
  31.         for ( int sexi=0; sexi<fields; ++sexi ) \
  32.             data[sexi] = bytesex(data[sexi]); \
  33.     } \
  34.  
  35. #define bytesex_table(data, objs, fields, V)    \
  36.     if ( BYTE_ORDER != gender_table[V] ) { \
  37.         for ( int sexi=0; sexi<objs; ++sexi ) { \
  38.             for ( int sexj=0; sexj<fields; ++sexj ) \
  39.                 data[sexi][sexj] = bytesex(data[sexi][sexj]); \
  40.         } \
  41.     } \
  42.  
  43. // Try to find a given Doom file.
  44.  
  45. int Checkforfile(char *doomfile, char *arg1, char *doomext, char *type, FILE **fp)
  46. {
  47.     EBool commandname = NO, found;
  48.     char filename[80];
  49.  
  50.     // If there's nothing specified in the ini, use the command line.
  51.     if (strlen(doomfile) == 0 || strlen(arg1) != 0)
  52.         commandname = YES;
  53.  
  54.     // This (attempts to) find the doom.exe file
  55.     if (commandname == YES)
  56.     {
  57.         strcpy(filename, arg1);
  58.         strcat(filename, "doom");
  59.         strcat(filename, doomext);
  60.     }
  61.     else
  62.         strcpy(filename, doomfile);
  63.  
  64.     printf("Checking %s for doom%s...  ", filename, doomext);
  65.     fflush(stdout);
  66.     if ((*fp = fopen(filename, type)) == NULL)
  67.     {
  68.         // Cheap kludge.  Swap a 2 in on the end of the filename (change from
  69.         // "doom.exe" to "doom2.exe", if we're running command-line AND
  70.         // the name extension isn't "hack.exe", which it will be for the
  71.         // backup file.
  72.         if ((commandname == YES) && (strlen(doomext) < 7))
  73.         {
  74.             // Slip a "2" into the filename before the extension.
  75.             strcpy(strstr(filename, doomext), "2");
  76.             strcat(filename, doomext);
  77.             printf("not found!\r\nChecking %s for doom2%s...", filename, doomext);
  78.             if ((*fp = fopen(filename, "r+b")) == NULL)
  79.                 found = NO;
  80.             else
  81.                 found = YES;
  82.         }
  83.         else
  84.             found = NO;
  85.     }
  86.     else
  87.         found = YES;
  88.  
  89.     if (found == YES)
  90.     {
  91.         printf("found!\r\n");
  92.         strcpy(doomfile, filename);
  93.         return 0;
  94.     }
  95.     else
  96.     {
  97.         printf("not found!\r\n");
  98.         return -1;
  99.     }
  100. }
  101.  
  102. // Converts a patch file from 1.2 to 1.666 format.
  103.  
  104. void Convertpatch(FILE *patchp, char patchformat)
  105. {
  106.     int i, j;
  107.     long buffer[22];
  108.     char forder[7] = {NORMALFRAME, MOVEFRAME, INJUREFRAME, CLOSEATTACKFRAME,
  109.                             FARATTACKFRAME, DEATHFRAME, EXPLDEATHFRAME};
  110.     char sorder[5] = {ALERTSOUND, ATTACKSOUND, PAINSOUND, DEATHSOUND, ACTSOUND};
  111.  
  112.     for (i=0; i<numobj[THING][DOOM1_2]-1; i++)
  113.     {
  114.         fread(buffer, size[THING][DOOM1_2], 1, patchp);
  115.         for (j=0; j<5; j++)
  116.             buffer[sorder[j]] = soundconvar[buffer[sorder[j]]];
  117.         for (j=0; j<7; j++)
  118.             buffer[forder[j]] = frameconvar[buffer[forder[j]]];
  119.         memcpy(thingdata[thingconvar[i]], buffer, size[THING][DOOM1_2]);
  120.     }
  121.  
  122.     fread(maxammodata, size[AMMO][DOOM1_2],   numobj[AMMO][DOOM1_2], patchp);
  123.     fread(perammodata, size[AMMO][DOOM1_2],   numobj[AMMO][DOOM1_2], patchp);
  124.  
  125.     for (i=0; i<numobj[WEAPON][DOOM1_2]; i++)
  126.     {
  127.         fread(buffer,  size[WEAPON][DOOM1_2], 1, patchp);
  128.         for (j=0; j<5; j++)
  129.             buffer[j+1] = frameconvar[buffer[j+1]];
  130.         memcpy(weapondata[i], buffer, size[WEAPON][DOOM1_2]);
  131.     }
  132.  
  133.     if (patchformat == 2)
  134.     {
  135.         for (i=0; i<numobj[FRAME][DOOM1_2]; i++)
  136.         {
  137.             fread(buffer, size[FRAME][DOOM1_2], 1, patchp);
  138.             buffer[SPRITENUM] = spriteconvar[buffer[SPRITENUM]];
  139.             buffer[NEXTFRAME] = frameconvar[buffer[NEXTFRAME]];
  140.             memcpy(framedata[frameconvar[i]], buffer, size[FRAME][DOOM1_2]);
  141.         }
  142.     }
  143. }
  144.  
  145. // Creates a save patch file that contains only differences between
  146. // the current doom exe and the backup one.  Note: this function is
  147. // long and unwieldy.  Inelegant code sucks.
  148.  
  149. int CreateDiffSave(FILE *patchp)
  150. {
  151.     long (*thingorig )[THING_FIELDS];
  152.     long (*frameorig )[FRAME_FIELDS];
  153.     long (*soundorig )[SOUND_FIELDS];
  154.     long (*weaponorig)[WEAPON_FIELDS];
  155.     long *spriteorig;
  156.     long *maxammoorig;
  157.     long *perammoorig;
  158.     char *textorigp;
  159.  
  160.     EBool nameprinted;
  161.     int i, j;
  162.  
  163.     // Initialize memory according to largest values needed, for loading
  164.     // original data from the unaltered exe file.
  165.     thingorig  = new long[numobj[THING][version]][THING_FIELDS];
  166.     soundorig  = new long[numobj[SOUND][version]][SOUND_FIELDS];
  167.     frameorig  = new long[numobj[FRAME][version]][FRAME_FIELDS];
  168.     spriteorig = new long[numobj[SPRITE][version]];
  169.     maxammoorig= new long[numobj[AMMO][version]];
  170.     perammoorig= new long[numobj[AMMO][version]];
  171.     weaponorig = new long[numobj[WEAPON][version]][WEAPON_FIELDS];
  172.     textorigp  = new char[size[TXT][version]];
  173.  
  174.     // Check if any of the memory didn't come though, and return if so.
  175.     if (thingorig == NULL || soundorig == NULL || frameorig == NULL ||
  176.          spriteorig == NULL || maxammoorig == NULL || perammoorig == NULL ||
  177.          weaponorig == NULL || textorigp == NULL)
  178.         return -1;
  179.  
  180.     // Move all of the data that's currently here (original data)
  181.     // to a safe place, so we can load in the other exe data.
  182.     memmove(thingorig,   thingdata,   numobj[THING][version]*THING_FIELDS*4);
  183.     memmove(soundorig,   sounddata,   numobj[SOUND][version]*SOUND_FIELDS*4);
  184.     memmove(frameorig,   framedata,   numobj[FRAME][version]*FRAME_FIELDS*4);
  185.     memmove(spriteorig,  spritedata,  numobj[SPRITE][version]*4);
  186.     memmove(maxammoorig, maxammodata, numobj[AMMO][version]*4);
  187.     memmove(perammoorig, perammodata, numobj[AMMO][version]*4);
  188.     memmove(weaponorig,  weapondata,  numobj[WEAPON][version]*WEAPON_FIELDS*4);
  189.     memmove(textorigp,   textdatap,   size[TXT][version]);
  190.  
  191.     Loaddoom(doombakfp);
  192.  
  193.     // Check each element in each array of the original data vs the
  194.     // info found in the backup exe.  If they're different, copy the old
  195.     // value to the patch file.
  196.     for (i=0; i<numobj[THING][version]-1; i++)
  197.     {
  198.         nameprinted = NO;
  199.         for (j=0; j<THING_FIELDS; j++)
  200.         {
  201.             if (thingorig[i][j] != thingdata[i][j])
  202.             {
  203.                 if (nameprinted == NO)
  204.                 {
  205.                     fprintf(patchp, "\nThing %d (%s)\n", i+1, namelist[i]);
  206.                     nameprinted = YES;
  207.                 }
  208.                 fprintf(patchp, "%s = %ld\n", thingfields[j], thingorig[i][j]);
  209.             }
  210.         }
  211.     }
  212.  
  213.     for (i=0; i<numobj[SOUND][version]; i++)
  214.     {
  215.         nameprinted = NO;
  216.         for (j=0; j<SOUND_FIELDS; j++)
  217.         {
  218.             if (soundorig[i][j] != sounddata[i][j])
  219.             {
  220.                 if (nameprinted == NO)
  221.                 {
  222.                     fprintf(patchp, "\nSound %d\n", i+1);
  223.                     nameprinted = YES;
  224.                 }
  225.                 fprintf(patchp, "%s = %ld\n", soundfields[j], soundorig[i][j]);
  226.             }
  227.         }
  228.     }
  229.  
  230.     for (i=0; i<numobj[FRAME][version]; i++)
  231.     {
  232.         nameprinted = NO;
  233.         for (j=0; j<FRAME_FIELDS; j++)
  234.         {
  235.             // I may draw flak for this one, but don't save the action
  236.             // pointers to the .deh file.  They're different every version,
  237.             // but make no difference at all.
  238.             if ((frameorig[i][j] != framedata[i][j]) &&
  239.                  (j != ACTIONPTR))
  240.             {
  241.                 if (nameprinted == NO)
  242.                 {
  243.                     fprintf(patchp, "\nFrame %d\n", i);
  244.                     nameprinted = YES;
  245.                 }
  246.                 fprintf(patchp, "%s = %ld\n", framefields[j], frameorig[i][j]);
  247.             }
  248.         }
  249.     }
  250.  
  251.     for (i=0; i<numobj[SPRITE][version]; i++)
  252.         if (spriteorig[i] != spritedata[i])
  253.             fprintf(patchp, "\nSprite %d\nOffset = %ld\n", i, spriteorig[i]);
  254.  
  255.     for (i=0; i<numobj[AMMO][version]; i++)
  256.     {
  257.         nameprinted = NO;
  258.         if (maxammoorig[i] != maxammodata[i])
  259.         {
  260.             fprintf(patchp, "\nAmmo %d (%s)\n", i, ammolist[i]);
  261.             nameprinted = YES;
  262.             fprintf(patchp, "Max ammo = %ld\n", maxammoorig[i]);
  263.         }
  264.  
  265.         if (perammoorig[i] != perammodata[i])
  266.         {
  267.             if (nameprinted == NO)
  268.                 fprintf(patchp, "\nAmmo %d (%s)\n", i, ammolist[i]);
  269.             fprintf(patchp, "Per ammo = %ld\n", perammoorig[i]);
  270.         }
  271.     }
  272.  
  273.     for (i=0; i<numobj[WEAPON][version]; i++)
  274.     {
  275.         nameprinted = NO;
  276.         for (j=0; j<WEAPON_FIELDS; j++)
  277.         {
  278.             if (weaponorig[i][j] != weapondata[i][j])
  279.             {
  280.                 if (nameprinted == NO)
  281.                 {
  282.                     fprintf(patchp, "\nWeapon %d (%s)\n", i, weaponlist[i]);
  283.                     nameprinted = YES;
  284.                 }
  285.                 fprintf(patchp, "%s = %ld\n", weaponfields[j], weaponorig[i][j]);
  286.             }
  287.         }
  288.     }
  289.  
  290.     // Write the text LAST, since it's the most error-prone part, and
  291.     // hardest to recover from.  And skip the last few chars, since some-
  292.     // thing wierd happens if I include them.  Hmmm..
  293.     if ( Lnx_DOOM ) {
  294.         lnx_savetxt(textorigp, textdatap, size[TXT][version], patchp);
  295.     } else {
  296.         for (i=0; i<size[TXT][version]-4; i += (strlen(textorigp+i) & ~3) + 4) {
  297.             if (strcmp(textorigp+i, textdatap+i) != 0) {
  298.                 fprintf(patchp, "\nText %d %d\n%s%s\n", 
  299.         strlen(textdatap+i), strlen(textorigp+i), textdatap+i, textorigp+i);
  300.             }
  301.         }
  302.     }
  303.  
  304.     // Move all of the data back to its original location.
  305.     memmove(thingdata,   thingorig,   numobj[THING][version]*THING_FIELDS*4);
  306.     memmove(sounddata,   soundorig,   numobj[SOUND][version]*SOUND_FIELDS*4);
  307.     memmove(framedata,   frameorig,   numobj[FRAME][version]*FRAME_FIELDS*4);
  308.     memmove(spritedata,  spriteorig,  numobj[SPRITE][version]*4);
  309.     memmove(maxammodata, maxammoorig, numobj[AMMO][version]*4);
  310.     memmove(perammodata, perammoorig, numobj[AMMO][version]*4);
  311.     memmove(weapondata,  weaponorig,  numobj[WEAPON][version]*WEAPON_FIELDS*4);
  312.     memmove(textdatap,   textorigp,   size[TXT][version]);
  313.  
  314.     // Free up the memory
  315.     delete thingorig;
  316.     delete soundorig;
  317.     delete frameorig;
  318.     delete spriteorig;
  319.     delete maxammoorig;
  320.     delete perammoorig;
  321.     delete weaponorig;
  322.     delete textorigp;
  323.  
  324.     return 0;
  325. }
  326.  
  327. // Creates the back-up file that gets hacked, copied straight from the
  328. // original Doom file.
  329.  
  330. void CreateDoomhack(void)
  331. {
  332.     char *buffer;
  333.     long cursize = 0;
  334.     long totalsize;
  335.  
  336.     // Get some space in memory to use as a copy place.
  337.     if ((buffer = new char[4096]) == NULL)
  338.         AbortProg("in GetDoomFiles!");
  339.  
  340.     // Open the new file, find the size of the original exe file.
  341.     printf("\r\nCreating %s...\r\n", doomexe);
  342.     doomexefp = fopen(doomexe, "wb");
  343.     fseek(doombakfp, 0, SEEK_END);
  344.     totalsize = ftell(doombakfp);
  345.     fseek(doombakfp, 0, SEEK_SET);
  346.  
  347.     // Continue copying chunks, as long as there are chunks to be
  348.     // copied.
  349.     while (cursize < totalsize - 4096)
  350.     {
  351.         fread(buffer, 4096, 1, doombakfp);
  352.         fwrite(buffer, 4096, 1, doomexefp);
  353.         cursize += 4096;
  354.     }
  355.     fread(buffer, (unsigned int)(totalsize-cursize), 1, doombakfp);
  356.     fwrite(buffer, (unsigned int)(totalsize-cursize), 1, doomexefp);
  357.     fclose(doomexefp);
  358.  
  359.     delete[] buffer;
  360. }
  361.  
  362. // Finds the doom(2).exe and doom(2).wad files, sets a pointer
  363. // to the doom.exe file.
  364.  
  365. int GetDoomFiles(char *arg1)
  366. {
  367.     char wadstring[5];
  368.     char keypress;
  369.     ResourceS entry = {0, 0, ""};
  370.  
  371.     // Check for the WAD and original exe.
  372.     if ((Checkforfile(doombak, arg1, ".exe", "rb", &doombakfp) == -1) ||
  373.          (Checkforfile(doomwad, arg1, ".wad", "rb", &doomwadfp) == -1))
  374.     {
  375.         printf("\nCannot find a necessary Doom file, aborting!\n\n");
  376.         Printoptions();
  377.         return -1;
  378.     }
  379.  
  380.     // Check for the hack exe.  If it's not there, ask the user if he
  381.     // wants to create it.
  382.     if (Checkforfile(doomexe, arg1, "hack.exe", "r+b", &doomexefp) == -1)
  383.     {
  384.         puts("\nCannot find the backup exe file to edit.  DeHackEd will create");
  385.         puts("a copy of your Doom exe file and edit the copy rather than the");
  386.         printf("main exe file itself.  Do you want to do this?  ");
  387.         keypress = getch();
  388.         if (tolower(keypress) == 'y')
  389.         {
  390.             CreateDoomhack();
  391.             if (Checkforfile(doomexe, arg1, "hack.exe", "r+b", &doomexefp) == -1)
  392.             {
  393.                 puts("\nCreation failed!");
  394.                 return -1;
  395.             }
  396.         }
  397.         else
  398.             return -1;
  399.     }
  400.  
  401.     // Compare file sizes.  Who knows what fun things would happen if they
  402.     // were different versions?? Yuck!
  403.     fseek(doomexefp, 0, SEEK_END);
  404.     fseek(doombakfp, 0, SEEK_END);
  405.     if (ftell(doomexefp) != ftell(doombakfp))
  406.     {
  407.         printf("\nThe hacking exe file (%s) and regular exe file\n", doomexe);
  408.         printf("(%s) are not from the same version of Doom.  Would\n", doombak);
  409.         puts("you like to erase the old hacking exe file and create a new one");
  410.         printf("for your current Doom version?  ");
  411.         keypress = getch();
  412.         if (tolower(keypress) == 'y')
  413.         {
  414.             CreateDoomhack();
  415.             if (Checkforfile(doomexe, arg1, "hack.exe", "r+b", &doomexefp) == -1)
  416.             {
  417.                 puts("\nCreation failed!");
  418.                 return -1;
  419.             }
  420.         }
  421.         else
  422.             return -1;
  423.     }
  424.  
  425.     // Do doom.wad checking...
  426.     fseek(doomwadfp, 0, SEEK_SET);
  427.     fread(wadstring, 4, 1, doomwadfp);
  428.     wadstring[4] = 0;
  429.     fseek(doomwadfp, 0, SEEK_END);
  430.  
  431.     // Check WAD size, the first 4 bytes, and a random entry in the WAD.
  432.     // You are under strict orders not to change this check.
  433.     if (ftell(doomwadfp) < 8000000L || (strcmp(wadstring, "IWAD") != 0)
  434.          || (Searchforentry("BFS1A0", &entry) == 0))
  435.     {
  436.         puts("A registered Doom WAD file has not been detected.");
  437.         puts("If you have not registered Doom, you must do so before");
  438.         puts("you can use this program.  If you have registered Doom");
  439.         puts("and are still getting this error, please contact Greg Lewis");
  440.         puts("at gregl@umich.edu about this problem.");
  441.         return -1;
  442.     }
  443.     return 0;
  444. }
  445.  
  446. // Gets the next line of the patch file that's being loaded
  447.  
  448. #define whitespace(c)    ((c == ' ')||(c == '\t')||(c == '\r')||(c == '\n'))
  449.  
  450. int GetNextLine(char *nextline, int *numlines, FILE *patchp)
  451. {
  452.     char buffer[512], *line;
  453.     int i;
  454.  
  455.     while ( fgets(buffer, 511, patchp) ) {
  456.         ++(*numlines);
  457.         for ( i=strlen(buffer); i && whitespace(buffer[i-1]); --i );
  458.         buffer[i]='\0';
  459.         for ( line=buffer; *line && whitespace(*line); ++line );
  460.         if ( !*line || (*line == '#') )
  461.             continue;
  462.         strcpy(nextline, line);
  463.         return(1);
  464.     }
  465.     return(0);
  466. }
  467.  
  468. // Loads the new text file format.  Similar to CreateDiffSave...
  469.  
  470. int LoadDiff(FILE *patchp)
  471. {
  472.     int tempversion, patchformat;
  473.     int  error = 0;
  474.     char *nextline, *line2;
  475.     int result;
  476.     int curType = 0;
  477.     int curNumber;
  478.     int numlines = 1;
  479.     EBool matched, valid, lnx_ok, AbortLoop = NO;
  480.     int i;
  481.     int length1, length2;
  482.     char *errormsg[3] = {"Line %d: No value after equal sign.",
  483.                                 "Line %d: No value before equal sign.",
  484.                                 "Line %d: Invalid single word line."};
  485.  
  486.     // Get memory for a line.
  487.     if ((nextline = new char[120]) == NULL)
  488.         AbortProg("in LoadDiff");
  489.  
  490.     // See Savepatch for file formats
  491.  
  492.     // Find an end-of-line somewere after the version number, which
  493.     // should have been read in the main LoadPatch routine.
  494.     while (fgetc(patchp) != '\n')
  495.         ;
  496.  
  497.     // Process the whole file one line at a time.  GetNextLine returns 0
  498.     // at EOF.
  499.     while (GetNextLine(nextline, &numlines, patchp) && !AbortLoop)
  500.     {
  501.         // Set matched to NO to be sure we catch any errors. Also assume it's
  502.         // valid unless flagged otherwise.
  503.         matched = NO;
  504.         valid = YES;
  505.  
  506.         // Parse the line the for spaces or equal signs.
  507.         result = ProcessLine(nextline, &line2);
  508.  
  509.         // Result determines what happened during parsing... whether we
  510.         // have an error (negative) or an equals sign (1) or a space after
  511.         // a word (2).
  512.         switch (result)
  513.         {
  514.             case 1:
  515.                 // Found an equals sign.  Check for all possible lvalues
  516.                 // and take the appropriate action.
  517.                 if (strcmpi(nextline, "doom version") == 0)
  518.                 {
  519.                     matched = YES;
  520.                     if ((sscanf(line2, "%d", &tempversion) == 0) ||
  521.                          ((tempversion != 12) && (tempversion != 16) &&
  522.                           (tempversion != 17) && (tempversion != 18) &&
  523.                           (tempversion != 19) && (tempversion != 20)))
  524.                     {
  525.                         sprintf(nextline, "Line %d: Invalid Doom version number, assuming 1.9.", numlines);
  526.                         AbortLoop = Printwindow(nextline, ERROR);
  527.                         error = 2;
  528.                         tempversion = 19;
  529.                     }
  530.                 }
  531.                 else if (strcmpi(nextline, "patch format") == 0)
  532.                 {
  533.                     matched = YES;
  534.                     if ((sscanf(line2, "%d", &patchformat) == 0) ||
  535.                          (patchformat != 5))
  536.                     {
  537.                         sprintf(nextline, "Line %d: Invalid patch format number, assuming format 5.", numlines);
  538.                         AbortLoop = Printwindow(nextline, ERROR);
  539.                         error = 2;
  540.                         patchformat = 5;
  541.                     }
  542.                 }
  543.                 // For any particular mode that we're in there could be multiple
  544.                 // lvalues, stored in the "...fields" arrays.  Check them all.
  545.                 else if (curType == THING)
  546.                 {
  547.                     for (i=0; i<THING_FIELDS; i++)
  548.                         if ((strcmpi(nextline, thingfields[i]) == 0) &&
  549.                              !(version == DOOM1_12 && i == RESPAWNFRAME))
  550.                         {
  551.                             matched = YES;
  552.                             if (sscanf(line2, "%ld", &(thingdata[curNumber-1][i])) == 0)
  553.                                 valid = NO;
  554.                         }
  555.                 }
  556.                 else if (curType == FRAME)
  557.                 {
  558.                     for (i=0; i<FRAME_FIELDS; i++)
  559.                         if (strcmpi(nextline, framefields[i]) == 0)
  560.                         {
  561.                             matched = YES;
  562.                             if (sscanf(line2, "%ld", &(framedata[curNumber][i])) == 0)
  563.                                 valid = NO;
  564.                         }
  565.                 }
  566.                 else if (curType == SOUND)
  567.                 {
  568.                     for (i=0; i<SOUND_FIELDS; i++)
  569.                         if (strcmpi(nextline, soundfields[i]) == 0)
  570.                         {
  571.                             matched = YES;
  572.                             if (sscanf(line2, "%ld", &(sounddata[curNumber][i])) == 0)
  573.                                 valid = NO;
  574.                         }
  575.                 }
  576.                 else if (curType == SPRITE)
  577.                 {
  578.                     if (strcmpi(nextline, "offset") == 0)
  579.                     {
  580.                         matched = YES;
  581.                         if (sscanf(line2, "%ld", &(spritedata[curNumber])) == 0)
  582.                             valid = NO;
  583.                     }
  584.                 }
  585.                 else if (curType == AMMO)
  586.                 {
  587.                     if (strcmpi(nextline, "Max ammo") == 0)
  588.                     {
  589.                         matched = YES;
  590.                         if (sscanf(line2, "%ld", &(maxammodata[curNumber])) == 0)
  591.                             valid = NO;
  592.                     }
  593.                     else if (strcmpi(nextline, "Per ammo") == 0)
  594.                     {
  595.                         matched = YES;
  596.                         if (sscanf(line2, "%ld", &(perammodata[curNumber])) == 0)
  597.                             valid = NO;
  598.                     }
  599.  
  600.                 }
  601.                 else if (curType == WEAPON)
  602.                 {
  603.                     for (i=0; i<WEAPON_FIELDS; i++)
  604.                         if (strcmpi(nextline, weaponfields[i]) == 0)
  605.                         {
  606.                             matched = YES;
  607.                             if (sscanf(line2, "%ld", &(weapondata[curNumber][i])) == 0)
  608.                                 valid = NO;
  609.                         }
  610.                 }
  611.                 break;
  612.             case 2:
  613.                 // Found two words (or more) on a line.  Check for all
  614.                 // appropriate meanings and take the appropriate action.
  615.                 // These should be the section headers "Thing 1 (Player)".
  616.  
  617.                 // Compare the first word with all known object types
  618.                 // ("Thing", "Frame", etc...)
  619.                 matched = NO;
  620.                 for (i=0; i < NUMDATA; i++)
  621.                     if (strcmpi(nextline, datanames[i]) == 0)
  622.                     {
  623.                         matched = YES;
  624.                         curType = i;
  625.                         if (sscanf(line2, "%d", &curNumber) == 0)
  626.                         {
  627.                             sprintf(nextline, "Line %d: Unreadable %s number.", numlines, datanames[i]);
  628.                             AbortLoop = Printwindow(nextline, ERROR);
  629.                             error = 2;
  630.                         }
  631.                         else if ((curNumber < 0) ||
  632.                                     ((curType == TXT) && (curNumber > size[curType][version])) ||
  633.                                     ((curType != TXT) && (curNumber > numobj[curType][version])))
  634.                         {
  635.                             sprintf(nextline, "Line %d: %s number out of range.", numlines, datanames[i]);
  636.                             AbortLoop = Printwindow(nextline, ERROR);
  637.                             error = 2;
  638.                         }
  639.                     }
  640.  
  641.                 // This is a special case... gotta read the text directly
  642.                 // from the next line if we found a "Text" identifier.
  643.                 if (curType == TXT)
  644.                 {
  645.                     if (sscanf(line2, "%d %d", &length1, &length2) < 2)
  646.                     {
  647.                         sprintf(nextline, "Line %d: Unreadable length value, aborting read.", numlines);
  648.                         error = 1;
  649.                         goto ErrorJump;
  650.                     }
  651.                     else
  652.                     {
  653.                         line2 = new char[length1+1];
  654.  
  655.                         // Just so ya know, I really hate doing this.  Anyways,
  656.                         // read in whole string 1 char at a time, checking for
  657.                         // 0D 0A combos, which are really new-lines.
  658.                         for (i=0; i<length1; i++)
  659.                         {
  660.                             fread(line2+i, 1, 1, patchp);
  661.                             if (line2[i] == 0x0D)
  662.                             {
  663.                                 fread(line2+i, 1, 1, patchp);
  664.                                 line2[i] = '\n';
  665.                                 numlines++;
  666.                             } else if (line2[i] == 0x0A)
  667.                                     ++numlines;
  668.                         }
  669.                         line2[length1] = 0;
  670.  
  671.                         // OK, skip through the enter text section, trying to match
  672.                         // up the string.
  673.                         valid = NO;
  674.                         if ( Lnx_DOOM ) {
  675.                             valid = lnx_matchtxt(textdatap, size[TXT][version], line2, &curNumber);
  676.                         } else {
  677.                             for (i=0; i<size[TXT][version]-4; i += (strlen(textdatap+i) & ~3) + 4) {
  678.                                 if (strcmp(textdatap+i, line2) == 0)
  679.                                 {
  680.                                     curNumber = i;
  681.                                     valid = YES;
  682.                                     break;
  683.                                 }
  684.                             }
  685.                         }
  686.  
  687.                         lnx_ok = YES;
  688.                         if ( Lnx_DOOM ) {
  689.                             // String 2 must be shorter or equal to String 1.
  690.                             if ( length2 > length1 )
  691.                                 lnx_ok = NO;
  692.                         }
  693.                         if ( (valid == YES) && lnx_ok ) {
  694.                             // Just so ya know, I really hate doing this.  Anyways,
  695.                             // read in whole string 1 char at a time, checking for
  696.                             // 0D 0A combos, which are really new-lines.
  697.                             for (i=curNumber; i<curNumber+length2; i++)
  698.                             {
  699.                                 fread(textdatap+i, 1, 1, patchp);
  700.                                 if (textdatap[i] == 0x0D)
  701.                                 {
  702.                                     fread(textdatap+i, 1, 1, patchp);
  703.                                     textdatap[i] = '\n';
  704.                                     numlines++;
  705.                                 } else if (textdatap[i] == 0x0A)
  706.                                     ++numlines;
  707.                             }
  708.                             if ( Lnx_DOOM ) {
  709.                                 while ( length2 < length1 )
  710.                                     *(textdatap+(curNumber+length2++)) = ' ';
  711.                             }
  712.                             textdatap[curNumber+length2] = 0;
  713.                         }
  714.                         else
  715.                         {
  716.                             // Eat the rest of the patch...
  717.                             for ( i=0; i<length2; ++i ) {
  718.                                 fread(nextline, 1, 1, patchp);
  719.                                 if (nextline[0] == 0x0D) {
  720.                                     fread(nextline, 1, 1, patchp);
  721.                                     numlines++;
  722.                                 } else if (nextline[0] == 0x0A)
  723.                                     ++numlines;
  724.                             }
  725.                             sprintf(nextline, "Line %d: Unmatchable text string.", numlines);
  726.                             AbortLoop = Printwindow(nextline, ERROR);
  727.                             error = 2;
  728.                             valid = YES;
  729.                         }
  730.  
  731.                         delete line2;
  732.                     }
  733.  
  734.                     // Reset the current type
  735.                     curType = 0;
  736.                 }
  737.                 break;
  738.             case -1:
  739.             case -2:
  740.             case -3:
  741.                 // Error. Print error, and quit loading patch file entirely if
  742.                 // the user pressed Escape at the Printwindow prompt.
  743.                 sprintf(nextline, errormsg[-result-1], numlines);
  744.                 AbortLoop = Printwindow(nextline, ERROR);
  745.                 error = 2;
  746.                 break;
  747.         }
  748.  
  749.         // Whoops, it didn't match anything at all...
  750.         if ((matched == NO) && (result >= 0))
  751.         {
  752.             sprintf(nextline, "Line %d: Unknown line.", numlines);
  753.             AbortLoop = Printwindow(nextline, ERROR);
  754.             error = 2;
  755.         }
  756.  
  757.         // Whoops, invalid number somewhere...
  758.         if (valid == NO)
  759.         {
  760.             sprintf(nextline, "Line %d: Unreadable value in a %s field.", numlines, datanames[curType]);
  761.             AbortLoop = Printwindow(nextline, ERROR);
  762.             error = 2;
  763.         }
  764.     }
  765.  
  766.     // Jump straight here if we have an unrecoverable error.  Yeah, yeah, I
  767.     // know, but gotos are the easiest way.
  768. ErrorJump:
  769.  
  770.     // Print different messages according to results.
  771.     if (AbortLoop)
  772.     {
  773.         Printwindow("Patch file load aborted by user.", ERROR);
  774.         delete nextline;
  775.         return -1;
  776.     } else if (error == 0) {
  777.         delete nextline;
  778.         return 0;
  779.     } else if (error == 1) {
  780.         Printwindow(nextline, ERROR);
  781.         delete nextline;
  782.         return -1;
  783.     } else {
  784.         Printwindow("Patch file read, one or more errors detected.", ERROR);
  785.         delete nextline;
  786.         return -1;
  787.     }
  788. }
  789.  
  790.  
  791.  
  792. // Loads all of the data from the exe into the correct data structures.
  793.  
  794. void Loaddoom(FILE *exefp)
  795. {
  796.     int i;
  797.  
  798.     // Read Thing data
  799.     fseek(exefp, offset[THING][version], SEEK_SET);
  800.     for (i=0; i<numobj[THING][version]-1; i++) {
  801.         fread((void *)thingdata[i], size[THING][version], 1, exefp);
  802.         bytesex_row(thingdata[i], THING_FIELDS, version);
  803.     }
  804.  
  805.     // Read Sound data
  806.     fseek(exefp, offset[SOUND][version], SEEK_SET);
  807.     fread((void *)sounddata, size[SOUND][version], numobj[SOUND][version], exefp);
  808.     bytesex_table(sounddata, numobj[SOUND][version], SOUND_FIELDS, version);
  809.  
  810.     // Read Frame data
  811.     fseek(exefp, offset[FRAME][version], SEEK_SET);
  812.     fread((void *)framedata, size[FRAME][version], numobj[FRAME][version], exefp);
  813.     bytesex_table(framedata, numobj[FRAME][version], FRAME_FIELDS, version);
  814.  
  815.     // Read Sprite data
  816.     fseek(exefp, offset[SPRITE][version], SEEK_SET);
  817.     fread((void *)spritedata, size[SPRITE][version], numobj[SPRITE][version], exefp);
  818.     bytesex_row(spritedata, numobj[SPRITE][version], version);
  819.  
  820.     // Read Ammo data
  821.     fseek(exefp, offset[AMMO][version], SEEK_SET);
  822.     fread((void *)maxammodata, size[AMMO][version], numobj[AMMO][version], exefp);
  823.     bytesex_row(maxammodata, numobj[AMMO][version], version);
  824.     fread((void *)perammodata, size[AMMO][version], numobj[AMMO][version], exefp);
  825.     bytesex_row(perammodata, numobj[AMMO][version], version);
  826.  
  827.     // Read Weapon data
  828.     fseek(exefp, offset[WEAPON][version], SEEK_SET);
  829.     fread((void *)weapondata, size[WEAPON][version], numobj[WEAPON][version], exefp);
  830.     bytesex_table(weapondata, numobj[WEAPON][version], WEAPON_FIELDS, version);
  831.  
  832.     // Read Text data
  833.     if ( Lnx_DOOM ) {
  834.         lnx_loadtxt(textdatap, &size[TXT][version], 
  835.                         &numobj[TXT][version], exefp);
  836.     } else {
  837.         fseek(exefp, offset[TXT][version], SEEK_SET);
  838.         fread((void *)textdatap, size[TXT][version], 1, exefp);
  839.     }
  840. }
  841.  
  842. // Loads a patch file, the old format.
  843. // We don't support bytesex on the old-format patches. Is it worth the trouble?
  844.  
  845. int LoadOld(FILE *patchp)
  846. {
  847.     char buffer[80];
  848.     char tempversion, patchformat;
  849.     EBool error = YES;
  850.     int i, truepatch, offset;
  851.     EBool foundend = NO;
  852.  
  853.     fread(&tempversion, sizeof(char), 1, patchp);
  854.     fread(&patchformat, sizeof(char), 1, patchp);
  855.  
  856.     if (patchformat == 3)
  857.         strcpy(buffer, "Doom 1.6 beta patches are no longer valid.  Sorry!");
  858.     else if (patchformat != 4)
  859.         strcpy(buffer, "Bad patch version number!!");
  860.     else if (tempversion != 12 && version == DOOM1_2)
  861.         strcpy(buffer, "This patch requires a higher Doom version!");
  862.     else if (tempversion > 20 || tempversion < 16)
  863.         strcpy(buffer, "Bad Doom release number found!");
  864.     else
  865.         error = NO;
  866.  
  867.     if (error == YES)
  868.     {
  869.         Printwindow(buffer, ERROR);
  870.         return -1;
  871.     }
  872.  
  873.     // OK, if it passes all the tests, load the sucker in.
  874.     fread(thingdata,   size[THING][version],  numobj[THING][version]-1, patchp);
  875.     fread(maxammodata, size[AMMO][version],   numobj[AMMO][version], patchp);
  876.     fread(perammodata, size[AMMO][version],   numobj[AMMO][version], patchp);
  877.     fread(weapondata,  size[WEAPON][version], numobj[WEAPON][version], patchp);
  878.     fread(framedata,   size[FRAME][version],  numobj[FRAME][version], patchp);
  879.  
  880.     // These sections ARE different between Doom 2 and Doom 1.666...
  881.     // only load them in straight if they are the correct version.
  882.     if ((tempversion == 20 && truever == DOOM2_16) ||
  883.          (tempversion == 16 && truever == DOOM1_16) ||
  884.          (tempversion == 17 && truever == DOOM2_17) ||
  885.          (tempversion == 18 && truever == DOOM2_17A)||
  886.          (tempversion == 19 && truever == DOOM2_19))
  887.     {
  888.         fread(sounddata,  size[SOUND][version],  numobj[SOUND][version], patchp);
  889.         fread(spritedata, size[SPRITE][version], numobj[SPRITE][version], patchp);
  890.         fread(textdatap,  size[TXT][version],   1, patchp);
  891.     }
  892.     else if ((tempversion == 16 && (truever == DOOM2_17 || truever == DOOM2_17A)) ||
  893.                 (tempversion == 17 && (truever == DOOM1_16 || truever == DOOM2_17A)) ||
  894.                 (tempversion == 18 && (truever == DOOM1_16 || truever == DOOM2_17)))
  895.     {
  896.         // Otherwise try to convert them as best we can.
  897.         fread(sounddata,  size[SOUND][version],  numobj[SOUND][version], patchp);
  898.         fread(spritedata, size[SPRITE][version], numobj[SPRITE][version], patchp);
  899.         fread(textdatap,  size[TXT][version],   1, patchp);
  900.  
  901.         // Finds the text differences between the version it's loading
  902.         // and the version it's using.  Adds that to each sound/sprite.
  903.         switch (tempversion)
  904.         {
  905.             case 16: truepatch = DOOM1_16; break;
  906.             case 17: truepatch = DOOM2_17; break;
  907.             case 18: truepatch = DOOM2_17A; break;
  908.         }
  909.         offset = diffoffset[truever][2] - diffoffset[truepatch][2];
  910.  
  911.         for (i=0; i<numobj[SOUND][version]; i++)
  912.             sounddata[i][TEXTP] += offset;
  913.         for (i=0; i<numobj[SPRITE][version]; i++)
  914.             spritedata[i] += offset;
  915.  
  916.         // The following is ugly.  Depending on what version we're
  917.         // loading and what version we're in, tweak the text data
  918.         // to "fit" the new version.
  919.         if (diffoffset[truever][2] < diffoffset[truepatch][2])
  920.         {
  921.             memmove(&(textdatap[diffoffset[truever][0]]),
  922.                       &(textdatap[diffoffset[truepatch][0]]),
  923.                       diffoffset[truever][1] - diffoffset[truever][0]);
  924.             memmove(&(textdatap[diffoffset[truever][1]]),
  925.                       &(textdatap[diffoffset[truepatch][1]]),
  926.                       diffoffset[truever][2] - diffoffset[truever][1]);
  927.             memmove(&(textdatap[diffoffset[truever][2]]),
  928.                       &(textdatap[diffoffset[truepatch][2]]),
  929.                       size[TXT][version]-diffoffset[truepatch][2]);
  930.         }
  931.         else
  932.         {
  933.             memmove(&(textdatap[diffoffset[truever][2]]),
  934.                           &(textdatap[diffoffset[truepatch][2]]),
  935.                       size[TXT][version]-diffoffset[truepatch][2]-offset);
  936.             memmove(&(textdatap[diffoffset[truever][1]]),
  937.                       &(textdatap[diffoffset[truepatch][1]]),
  938.                       diffoffset[truever][2] - diffoffset[truever][1]);
  939.             memmove(&(textdatap[diffoffset[truever][0]]),
  940.                           &(textdatap[diffoffset[truepatch][0]]),
  941.                       diffoffset[truever][1] - diffoffset[truever][0]);
  942.         }
  943.  
  944.         // These two chunks make sure that the "title" strings are OK
  945.         // after the above tweaking.
  946.         for (i=diffoffset[truever][0]; i<diffoffset[truever][1]-2; i++)
  947.             if (textdatap[i] == 0 || foundend == YES)
  948.             {
  949.                 textdatap[i] = ' ';
  950.                     foundend = YES;
  951.             }
  952.         textdatap[i] = 0;
  953.  
  954.         foundend = NO;
  955.         for (i=diffoffset[truever][1]; i<diffoffset[truever][2]-2; i++)
  956.             if (textdatap[i] == 0 || foundend == YES)
  957.             {
  958.                 textdatap[i] = ' ';
  959.                 foundend = YES;
  960.             }
  961.         textdatap[i] = 0;
  962.     }
  963.     else
  964.         Printwindow("Patch made with different version exe. Loading compatible data.", INFO);
  965.  
  966.     return 0;
  967. }
  968.  
  969. // Loads a patch file
  970.  
  971. int Loadpatch(char *filename)
  972. {
  973.     FILE *patchp;
  974.     char tempversion, patchformat;
  975.     char fullname[150] = "";
  976.     EBool error = NO;
  977.     char idstring[30];
  978.     int i;
  979.  
  980.     // Fix the patch filename, put on the patchdir if we need to.
  981.     if ( (filename[0] != '/') && (filename[0] != '.') ) {
  982.         strcpy(fullname, patchdir);
  983.         if (fullname[0] != 0)
  984.             strcat(fullname, "/");
  985.     }
  986.     strcat(fullname, filename);
  987.  
  988.     // Tack on the extension if there isn't one.
  989.     Preparefilename(fullname);
  990.  
  991.     // Try to open the file, return an error if we can't for some reason.
  992.     if ((patchp = fopen(fullname, "rb")) == NULL)
  993.         if (errno == 2)
  994.         {
  995.             sprintf(filename, "File %s does not exist!", fullname);
  996.             Printwindow(filename, ERROR);
  997.             return -1;
  998.         }
  999.         else
  1000.         {
  1001.             sprintf(filename, "Error reading %s.", fullname);
  1002.             Printwindow(filename, ERROR);
  1003.             return -1;
  1004.         }
  1005.  
  1006.     // See Savepatch for file formats
  1007.  
  1008.     fseek(patchp, 0, SEEK_SET);
  1009.  
  1010.     // Read in the first character, and compare what it is to see if
  1011.     // we are dealing with one of the really old patch formats.
  1012.     fread(&tempversion, sizeof(char), 1, patchp);
  1013.  
  1014.     // Tempversion of 12 is Doom 1.2
  1015.     if (tempversion == 12)
  1016.     {
  1017.         // The only patch formats for 1.2 are 1 and 2.
  1018.         fread(&patchformat, sizeof(char), 1, patchp);
  1019.         if (patchformat != 1 && patchformat != 2)
  1020.         {
  1021.             Printwindow("Unknown patch file format!", ERROR);
  1022.             error = YES;
  1023.             goto ErrorJump;
  1024.         }
  1025.  
  1026.         // If the current version isn't Doom 1.2, we'll need to convert
  1027.         // the patch.
  1028.         if (version != DOOM1_2)
  1029.         {
  1030.             Convertpatch(patchp, patchformat);
  1031.             Printwindow("Patch converted from DHE 1.3 format.", INFO);
  1032.         }
  1033.         else
  1034.         {
  1035.             // Read in the Doom 1.2 format.
  1036.             for (i=0; i<numobj[THING][version]-1; i++)
  1037.                     fread(thingdata[i], size[THING][DOOM1_2], 1, patchp);
  1038.             fread(maxammodata, size[AMMO][DOOM1_2],   numobj[AMMO][DOOM1_2], patchp);
  1039.             fread(perammodata, size[AMMO][DOOM1_2],   numobj[AMMO][DOOM1_2], patchp);
  1040.             fread(weapondata,  size[WEAPON][DOOM1_2], numobj[WEAPON][DOOM1_2], patchp);
  1041.             if (patchformat == 2)
  1042.                 fread(framedata, size[FRAME][DOOM1_2], numobj[FRAME][DOOM1_2], patchp);
  1043.         }
  1044.     }
  1045.     else if (tempversion == 'P')
  1046.     {
  1047.         // OK, so it's one of the newer versions.
  1048.         fread(idstring, 24, 1, patchp);
  1049.         idstring[24] = 0;
  1050.         if (stricmp(idstring, "atch File for DeHackEd v") != 0)
  1051.         {
  1052.             Printwindow("This is not a DeHackEd patch file!", ERROR);
  1053.             error = YES;
  1054.             goto ErrorJump;
  1055.         }
  1056.  
  1057.         // Read in the version number, and convert it to an int to check
  1058.         // if we have a bad version.
  1059.         fread(idstring, 3, 1, patchp);
  1060.         idstring[3] = 0;
  1061.         idstring[4] = (idstring[0]-'0')*10+(idstring[2]-'0');
  1062.         if (idstring[4] > 23)
  1063.         {
  1064.             Printwindow("This patch file requires a newer DeHackEd release!", ERROR);
  1065.             error = YES;
  1066.         }
  1067.         else if (idstring[4] < 20)
  1068.         {
  1069.             Printwindow("This patch file has an incorrect version number!", ERROR);
  1070.             error = YES;
  1071.         }
  1072.         else if (idstring[4] == 23)
  1073.         {
  1074.             if (LoadDiff(patchp) == -1)
  1075.                 error = YES;
  1076.         }
  1077.         else
  1078.         {
  1079.             if (LoadOld(patchp) == -1)
  1080.                 error = YES;
  1081.         }
  1082.     }
  1083.     else
  1084.     {
  1085.         // The catch-all
  1086.         Printwindow("This is not a DeHackEd patch file!", ERROR);
  1087.         error = YES;
  1088.     }
  1089.  
  1090. ErrorJump:
  1091.     fclose(patchp);
  1092.  
  1093.     if (error == YES)
  1094.         return -1;
  1095.  
  1096.     sprintf(filename, "Patch file %s read.", fullname);
  1097.     Printwindow(filename, INFO);
  1098.     return 0;
  1099. }
  1100.  
  1101. // OldSave, saves in the old DeHackEd formats.
  1102. // We don't support bytesex on the old-format patches. Is it worth the trouble?
  1103.  
  1104. // Oldest patch file format:
  1105. //    (char) Doom.exe version #  (12, 16, etc.)
  1106. //    (char) Patch file format # (1 for DeHackEd 1.2 patch files, 2 for
  1107. //        DeHackEd 1.3 patch files)
  1108. //    The data.  Consists of the Thing data, maxAmmo data, perAmmo data,
  1109. //        Weapon data, and if it's patch version 2, Frame data.
  1110.  
  1111. // Old patch file format:
  1112. // (char)*28   "Patch File for DeHackEd v?.?"    Header, and DHE version #
  1113. // (char)      Version of Doom, 0 for 1.2, 1 for 1.666, 2 for 2.0
  1114. // (char)          Patch file format, 1, 2 and 3 are Old patches files,
  1115. //                        4 is the version for this one
  1116. // Data structures, written directly.  In this order:
  1117. //        thing, maxammo, perammo, weapon, frame, sound, sprite, text
  1118.  
  1119. int OldSave(char *filename, EBool Overwrite)
  1120. {
  1121.     FILE *patchp;
  1122.     char tempver;
  1123.     char format;
  1124.     char fullname[150] = "";
  1125.     int i;
  1126.  
  1127.     // Find out what version to call it.
  1128.     switch (truever)
  1129.     {
  1130.         case DOOM1_12:
  1131.             tempver = 12;
  1132.             break;
  1133.         case DOOM1_16:
  1134.             tempver = 16;
  1135.             break;
  1136.         case DOOM2_16:
  1137.             tempver = 20;
  1138.             break;
  1139.         case DOOM2_17:
  1140.             tempver = 17;
  1141.             break;
  1142.         case DOOMX_18:        // Linux Doom uses DOOM2_19 patches
  1143.         case DOOMS_18:
  1144.         case DOOM_SGI:
  1145.         case DOOM2_17A:
  1146.             tempver = 18;    // Yeah, this is a kludge
  1147.         case DOOM2_18:
  1148.             break;        // Not supported.
  1149.         case DOOM2_19:
  1150.             tempver = 19;
  1151.             break;
  1152.         case USERDEF:
  1153.             tempver = 19;    // What do we do with USERDEF?  FIXME!!
  1154.             break;
  1155.     }
  1156.  
  1157.     // Try to switch to the patch directory
  1158.     i = chdir(patchdir);
  1159.     if (i == -1 && patchdir[0] != 0)
  1160.     {
  1161.         sprintf(filename, "Patch directory %s not found!", patchdir);
  1162.         return ERROR;
  1163.     }
  1164.     chdir(curdir);
  1165.  
  1166.     // Prepend the patch directory if necessary.
  1167.     if ( (filename[0] != '/') && (filename[0] != '.') ) {
  1168.         strcpy(fullname, patchdir);
  1169.         if (fullname[0] != 0)
  1170.             strcat(fullname, "/");
  1171.     }
  1172.     strcat(fullname, filename);
  1173.  
  1174.     // Turn it into a valid filename, add the extension if necessary.
  1175.     Preparefilename(fullname);
  1176.  
  1177.     // If we open it and it's not NULL, it already exists.  Return an
  1178.     // error in this case, or just continue if the Overwrite variable is
  1179.     // set.
  1180.     if ((patchp = fopen(fullname, "rb")) != NULL)
  1181.     {
  1182.         if (Overwrite == NO)
  1183.         {
  1184.             fclose(patchp);
  1185.             return -1;
  1186.         }
  1187.     }
  1188.     else if (errno != 2)
  1189.     {
  1190.         sprintf(filename, "Error writing %s!", fullname);
  1191.         return ERROR;
  1192.     }
  1193.  
  1194.     // Close the file for read-only and reopen it for writing.
  1195.     fclose(patchp);
  1196.     patchp = fopen(fullname, "wb");
  1197.  
  1198.     if (version == DOOM1_2)
  1199.         format = 2;
  1200.     else
  1201.     {
  1202.         // This must continue to be "v2.0" even though we're on a higher
  1203.         // version, because my compatibility scheme backfired.  DHE v2.0
  1204.         // checks for this string, so it's gotta be there.  Urg.
  1205.         format = 4;
  1206.         fwrite("Patch File for DeHackEd v2.0", 28, 1, patchp);
  1207.     }
  1208.  
  1209.     fwrite(&tempver, sizeof(char), 1, patchp);
  1210.     fwrite(&format, sizeof(char), 1, patchp);
  1211.     for (i=0; i<numobj[THING][version]-1; i++)
  1212.         fwrite(thingdata[i], size[THING][version], 1, patchp);
  1213.     fwrite(maxammodata, size[AMMO][version],   numobj[AMMO][version], patchp);
  1214.     fwrite(perammodata, size[AMMO][version],   numobj[AMMO][version], patchp);
  1215.     fwrite(weapondata,  size[WEAPON][version], numobj[WEAPON][version], patchp);
  1216.     fwrite(framedata,   size[FRAME][version],  numobj[FRAME][version], patchp);
  1217.  
  1218.     if (version != DOOM1_2)
  1219.     {
  1220.         fwrite(sounddata,  size[SOUND][version],  numobj[SOUND][version], patchp);
  1221.         fwrite(spritedata, size[SPRITE][version], numobj[SPRITE][version], patchp);
  1222.         fwrite(textdatap,  size[TXT][version],   1, patchp);
  1223.     }
  1224.  
  1225.     fclose(patchp);
  1226.     sprintf(filename, "Patch file %s written.", fullname);
  1227.     return INFO;
  1228. }
  1229.  
  1230. // Parses the config file
  1231.  
  1232. void Parseconfigfile(void)
  1233. {
  1234.     FILE *cfgfp;
  1235.     char nextline[256];
  1236.     char *line2;
  1237.     int i;
  1238.     int numlines = 1;
  1239.     EBool match = NO;
  1240.     int tempver, result;
  1241.     char *options[19] = {"pathname",
  1242.                                 "editname",
  1243.                                 "normalname",
  1244.                                 "wadname",
  1245.                                 "params",
  1246.                                 "patchdir",
  1247.                                 "version",
  1248.                                 "size",
  1249.                                 "thingoff",
  1250.                                 "soundoff",
  1251.                                 "frameoff",
  1252.                                 "spriteoff",
  1253.                                 "ammooff",
  1254.                                 "weaponoff",
  1255.                                 "textoff",
  1256.                                 "codepoff",
  1257.                                 "sbaddress",
  1258.                                 "sbirq",
  1259.                                 "sbdma"};
  1260.     char *strptrs[6] = {doompath, doomexe, doombak, doomwad, doomargs,
  1261.                               patchdir};
  1262.  
  1263.     if ((cfgfp = fopen("dehacked.ini", "rt")) == NULL)
  1264.     {
  1265.         puts("dehacked.ini not found.");
  1266.         return;
  1267.     }
  1268.  
  1269.     while (GetNextLine(nextline, &numlines, cfgfp))
  1270.     {
  1271.         // Parse the line the for spaces or equal signs.
  1272.         result = ProcessLine(nextline, &line2);
  1273.  
  1274.         switch (result)
  1275.         {
  1276.             case 1:
  1277.                 for (i=0; i<19; i++)
  1278.                 {
  1279.                     if (strcmpi(nextline, options[i]) == 0)
  1280.                     {
  1281.                         match = YES;
  1282.                         switch (i)
  1283.                         {
  1284.                             case 0:
  1285.                             case 1:
  1286.                             case 2:
  1287.                             case 3:
  1288.                             case 4:
  1289.                             case 5:
  1290.                                 strcpy(strptrs[i], line2);
  1291.                                 break;
  1292.                             case 6:
  1293.                                 sscanf(line2, "%d", &tempver);
  1294.                                 if (tempver == 0)
  1295.                                     version = DOOM1_2;
  1296.                                 else if (tempver == 1)
  1297.                                     version = DOOM1_6;
  1298.                                 else if (tempver == 2)
  1299.                                     version = DOOM2_0;
  1300.                                 else if (tempver == 3)
  1301.                                     version = DOOM1_9;
  1302.                                 break;
  1303.                             case 7:
  1304.                                 sscanf(line2, "%ld", &doomsize);
  1305.                                 break;
  1306.                             case 8:
  1307.                             case 9:
  1308.                             case 10:
  1309.                             case 11:
  1310.                             case 12:
  1311.                             case 13:
  1312.                             case 14:
  1313.                             case 15:
  1314.                                 sscanf(line2, "%ld", &(offset[i-8]));
  1315.                                 break;
  1316.                         }
  1317.                     }
  1318.                 }
  1319.                 break;
  1320.             case -1: printf("Line %d: No value after equal sign.", numlines);
  1321.                 break;
  1322.             case -2: printf("Line %d: No value before equal sign.", numlines);
  1323.                 break;
  1324.             case 2:
  1325.             case -3: printf("Line %d: Invalid single-word line detected.", numlines);
  1326.                 break;
  1327.         }
  1328.  
  1329.         if (match == NO)
  1330.         {
  1331.             printf("Line %d: Cannot match variable \"%s\" in dehacked.ini!", numlines, nextline);
  1332.             break;
  1333.         }
  1334.         else
  1335.             match = NO;
  1336.     }
  1337.  
  1338.     fclose(cfgfp);
  1339. }
  1340.  
  1341. // Finds the filename in a path\filenames combination and turns it into
  1342. // familiar <8>.<3> combination if it's invalid.  Also appends ".deh"
  1343. // if there is no extension.
  1344.  
  1345. void Preparefilename(char *fullname)
  1346. {
  1347.     int i = 0;
  1348.     char *filename, *dot = NULL;
  1349.  
  1350.     filename = fullname;
  1351.  
  1352.     // Filename is a pointer to the actual filename, without the path.  So
  1353.     // step through the full path/filename combo, and keep moving the
  1354.     // current location of filename whenever we come to a directory separator.
  1355.     while (fullname[i] != 0)
  1356.     {
  1357.         if (fullname[i] == '/')
  1358.             filename = fullname+i+1;
  1359.         i++;
  1360.     }
  1361.  
  1362.     // Try to find the extension of the filename
  1363.     for (i=0; i<strlen(filename); i++)
  1364.         if (filename[i] == '.')
  1365.         {
  1366.             dot = filename + i;
  1367.             break;
  1368.         }
  1369.  
  1370.     if (dot == NULL)
  1371.     {
  1372.         // OK, no extension at all, so add our own at the correct place.
  1373.         if (strlen(filename) > 8)
  1374.             filename[8] = 0;
  1375.         strcat(filename, ".deh");
  1376.     }
  1377.     else
  1378.     {
  1379.         if (dot - filename > 8)
  1380.         {
  1381.             strncpy(filename + 8, dot, 4);
  1382.             filename[12] = 0;
  1383.         }
  1384.         else
  1385.             dot[4] = 0;
  1386.     }
  1387. }
  1388.  
  1389. // This procedure processes a line from a patch file.  If an equals sign
  1390. // exists in the input line, the following is done:
  1391.  
  1392. //     *next line
  1393. //         \--->  first part  =  second part
  1394. //              zero byte --^    ^--- *line2
  1395.  
  1396. // If there is no equals sign, the following is done:
  1397.  
  1398. //     *next line
  1399. //         \--->  word1 word2 and other words
  1400. //         zero byte --^^--- *line2
  1401.  
  1402. // Return values:
  1403. //   1  Successful - found an equals sign
  1404. //   2  Successful - found a word
  1405. //  -1  No info after equals sign
  1406. //  -2  No value before equals sign
  1407. //  -3  No info after first word in line
  1408.  
  1409. int ProcessLine(char *nextline, char **line2)
  1410. {
  1411.     int i = 0, j = 0;
  1412.  
  1413.     // Search line for an =
  1414.     while (nextline[i] != 0 && nextline[i] != '=')
  1415.         i++;
  1416.  
  1417.     // If we found one...
  1418.     if (nextline[i] == '=')
  1419.     {
  1420.         // Search for the first non-space after the =.
  1421.         j = i--;
  1422.         while (isspace(nextline[++j]))
  1423.             ;
  1424.  
  1425.         // It was all whitespace, error... should be equal to something
  1426.         if (nextline[j] == 0)
  1427.             return -1;
  1428.  
  1429.         // Set line2 to the first non-space after an =
  1430.         *line2 = nextline+j;
  1431.  
  1432.         // Kill any whitespace before the =...
  1433.         while (i >= 0 && isspace(nextline[i]))
  1434.             i--;
  1435.  
  1436.         // It was all whitespace, error... should be something before =
  1437.         if (i == -1)
  1438.             return -2;
  1439.  
  1440.         // OK, put in an end-of-string character to kill the space(s)
  1441.         nextline[i+1] = 0;
  1442.  
  1443.         // Successful
  1444.         return 1;
  1445.     }
  1446.     // Otherwise, the line should have two separate words on it
  1447.     else
  1448.     {
  1449.         // Search for first space on the line
  1450.         while (nextline[j] != 0 && !isspace(nextline[j]))
  1451.             j++;
  1452.  
  1453.         // Only one word on line, didn't find any spaces at all
  1454.         if (nextline[j] == 0)
  1455.             return -3;
  1456.  
  1457.         // Found some space(s), now search for the second word
  1458.         i = j;
  1459.         while (isspace(nextline[++i]))
  1460.             ;
  1461.  
  1462.         // No non-spaces after the first word
  1463.         if (nextline[i] == 0)
  1464.             return -3;
  1465.  
  1466.         // Set this to the first letter of the second word
  1467.         *line2 = nextline+i;
  1468.  
  1469.         // Terminate the first word's string
  1470.         nextline[j] = 0;
  1471.  
  1472.         // Successful
  1473.         return 2;
  1474.     }
  1475. }
  1476.  
  1477. // Saves a patch file.
  1478.  
  1479. // Current patch file format:
  1480. // Patch file format:
  1481. // "Patch File for DeHackEd v?.?"    Header, and DeHackEd version #
  1482. // "Doom version = ??"                   Version of Doom
  1483. // "Patch format = ?"                      Patch file format, 5 is the current one
  1484. //
  1485. // Data structures, stored as text, in this order:
  1486. //        thing, sound, frame, sprite, ammo, weapon, text
  1487.  
  1488. int Savepatch(char *filename, EBool Overwrite)
  1489. {
  1490.     FILE *patchp;
  1491.     char tempver;
  1492.     char fullname[150] = "";
  1493.     int i;
  1494.  
  1495.     // Find out what version to call it.
  1496.     switch (truever)
  1497.     {
  1498.         case DOOM1_12:
  1499.             tempver = 12;
  1500.             break;
  1501.         case DOOM1_16:
  1502.             tempver = 16;
  1503.             break;
  1504.         case DOOM2_16:
  1505.             tempver = 20;
  1506.             break;
  1507.         case DOOM2_17:
  1508.             tempver = 17;
  1509.             break;
  1510.         case DOOMX_18:        // Linux Doom uses DOOM2_19 patches
  1511.         case DOOMS_18:
  1512.         case DOOM_SGI:
  1513.         case DOOM2_17A:
  1514.             tempver = 18;    // Yeah, this is a kludge
  1515.         case DOOM2_18:
  1516.             break;        // Not supported.
  1517.         case DOOM2_19:
  1518.             tempver = 19;
  1519.             break;
  1520.         case USERDEF:
  1521.             tempver = 19;    // What do we do with USERDEF?  FIXME!!
  1522.             break;
  1523.     }
  1524.  
  1525.     // Try to switch to the patch directory
  1526.     i = chdir(patchdir);
  1527.     if (i == -1 && patchdir[0] != 0)
  1528.     {
  1529.         sprintf(filename, "Patch directory %s not found!", patchdir);
  1530.         return ERROR;
  1531.     }
  1532.     chdir(curdir);
  1533.  
  1534.     // Prepend the patch directory if necessary.
  1535.     if ( (filename[0] != '/') && (filename[0] != '.') ) {
  1536.         strcpy(fullname, patchdir);
  1537.         if (fullname[0] != 0)
  1538.             strcat(fullname, "/");
  1539.     }
  1540.     strcat(fullname, filename);
  1541.  
  1542.     // Turn it into a valid filename, add the extension if necessary.
  1543.     Preparefilename(fullname);
  1544.  
  1545.     // If we open it and it's not NULL, it already exists.  Return an
  1546.     // error in this case, or just continue if the Overwrite variable is
  1547.     // set.
  1548.     if ((patchp = fopen(fullname, "rt")) != NULL)
  1549.     {
  1550.         if (Overwrite == NO)
  1551.         {
  1552.             fclose(patchp);
  1553.             return -1;
  1554.         }
  1555.     }
  1556.     else if (errno != 2)
  1557.     {
  1558.         sprintf(filename, "Error writing %s!", fullname);
  1559.         return ERROR;
  1560.     }
  1561.  
  1562.     // Close the file for read-only and reopen it for writing.
  1563.     fclose(patchp);
  1564.     patchp = fopen(fullname, "wt");
  1565.  
  1566.     // This is the header for the patch files.
  1567.     fprintf(patchp, "Patch File for DeHackEd v2.3\n\n");
  1568.     fprintf(patchp, "# Note: Use the pound sign ('#') to start comment lines.\n\n");
  1569.     fprintf(patchp, "Doom version = %d\n", tempver);
  1570.     fprintf(patchp, "Patch format = 5\n\n");
  1571.  
  1572.     // Do the bulk of the saving.
  1573.     if (CreateDiffSave(patchp) == 0)
  1574.     {
  1575.         fclose(patchp);
  1576.         sprintf(filename, "Patch file %s written.", fullname);
  1577.         return INFO;
  1578.     }
  1579.     else
  1580.     {
  1581.         fclose(patchp);
  1582.         AbortProg("in SavePatch");
  1583.         return ERROR;
  1584.     }
  1585. }
  1586.  
  1587. // This searches in the doom.wad file for an entry name.
  1588. // Returns 1 on success, 0 on failure.
  1589.  
  1590. int Searchforentry(char *name, ResourceS *entry)
  1591. {
  1592.     unsigned long dirlength, dirstart;
  1593.     int i;
  1594.  
  1595.     // Read in the directory info (start of directory and number of
  1596.     // entries).
  1597.     fseek(doomwadfp, 4, SEEK_SET);
  1598.     fread(&dirlength, 4, 1, doomwadfp);
  1599.     fread(&dirstart,  4, 1, doomwadfp);
  1600.  
  1601.     // Correct byte ordering... (wad is in little_endian form)
  1602.     if ( NEED_SEX ) {
  1603.         bytesex(dirlength);
  1604.         bytesex(dirstart);
  1605.     }
  1606.  
  1607.     // Go to start of directory.
  1608.     fseek(doomwadfp, dirstart, SEEK_SET);
  1609.  
  1610.     // Scan through the directory, one by one, reading each in and
  1611.     // checking if it matches the name we're seeking.
  1612.     for (i=0; i<dirlength; i++)
  1613.     {
  1614.         fread(entry, 16, 1, doomwadfp);
  1615.         if (strncmp(entry->resname, name, strlen(name)) == 0)
  1616.         {
  1617.             entry->resname[8] = 0;
  1618.             if ( NEED_SEX ) {
  1619.                 bytesex(entry->resstart);
  1620.                 bytesex(entry->reslength);
  1621.             }
  1622.             return 1;
  1623.         }
  1624.     }
  1625.  
  1626.     return 0;
  1627. }
  1628.  
  1629. // Write only the changeable data structures to the doom.exe
  1630.  
  1631. void Writedoom(void)
  1632. {
  1633.     int i;
  1634.  
  1635.     // Write the Thing stuff
  1636.     fseek(doomexefp, offset[THING][version], SEEK_SET);
  1637.     for (i=0; i<numobj[THING][version]-1; i++) {
  1638.         bytesex_row(thingdata[i], THING_FIELDS, version);
  1639.         fwrite(thingdata[i], size[THING][version], 1, doomexefp);
  1640.         bytesex_row(thingdata[i], THING_FIELDS, version);
  1641.     }
  1642.  
  1643.     // Write Sound data
  1644.     fseek(doomexefp, offset[SOUND][version], SEEK_SET);
  1645.     bytesex_table(sounddata, numobj[SOUND][version], SOUND_FIELDS, version);
  1646.     fwrite(sounddata, size[SOUND][version], numobj[SOUND][version], doomexefp);
  1647.     bytesex_table(sounddata, numobj[SOUND][version], SOUND_FIELDS, version);
  1648.  
  1649.     // Write Frame data
  1650.     fseek(doomexefp, offset[FRAME][version], SEEK_SET);
  1651.     bytesex_table(framedata, numobj[FRAME][version], FRAME_FIELDS, version);
  1652.     fwrite(framedata, size[FRAME][version], numobj[FRAME][version], doomexefp);
  1653.     bytesex_table(framedata, numobj[FRAME][version], FRAME_FIELDS, version);
  1654.  
  1655.     // Write Sprite data
  1656.     fseek(doomexefp, offset[SPRITE][version], SEEK_SET);
  1657.     bytesex_row(spritedata, numobj[SPRITE][version], version);
  1658.     fwrite(spritedata, size[SPRITE][version], numobj[SPRITE][version], doomexefp);
  1659.     bytesex_row(spritedata, numobj[SPRITE][version], version);
  1660.  
  1661.     // Write Ammo data
  1662.     fseek(doomexefp, offset[AMMO][version], SEEK_SET);
  1663.     bytesex_row(maxammodata, numobj[AMMO][version], version);
  1664.     bytesex_row(perammodata, numobj[AMMO][version], version);
  1665.     fwrite(maxammodata, size[AMMO][version], numobj[AMMO][version], doomexefp);
  1666.     fwrite(perammodata, size[AMMO][version], numobj[AMMO][version], doomexefp);
  1667.     bytesex_row(maxammodata, numobj[AMMO][version], version);
  1668.     bytesex_row(perammodata, numobj[AMMO][version], version);
  1669.  
  1670.     // Write Weapon data
  1671.     fseek(doomexefp, offset[WEAPON][version], SEEK_SET);
  1672.     bytesex_table(weapondata, numobj[WEAPON][version], WEAPON_FIELDS, version);
  1673.     fwrite(weapondata, size[WEAPON][version], numobj[WEAPON][version], doomexefp);
  1674.     bytesex_table(weapondata, numobj[WEAPON][version], WEAPON_FIELDS, version);
  1675.  
  1676.     // Write Text data
  1677.     if ( Lnx_DOOM ) {
  1678.         lnx_writetxt(doomexefp);
  1679.     } else {
  1680.         fseek(doomexefp, offset[TXT][version], SEEK_SET);
  1681.         fwrite(textdatap, size[TXT][version], 1, doomexefp);
  1682.     }
  1683.  
  1684.     // Make sure the file is executable. :)
  1685.     fchmod(fileno(doomexefp), 0755);
  1686. }
  1687.  
  1688. // Write only the changeable data structures from the doom.exe
  1689.  
  1690. void Dumpdata(void)
  1691. {
  1692.     FILE *output;
  1693.     int i;
  1694.  
  1695.     // Write the Thing stuff
  1696.     if ( !(output=fopen("ThingData", "w")) ) {
  1697.         Printwindow("Can't write to 'ThingData'", ERROR);
  1698.         return;
  1699.     }
  1700.     for (i=0; i<numobj[THING][version]-1; i++)
  1701.         fwrite(thingdata[i], size[THING][version], 1, output);
  1702.     fclose(output);
  1703.  
  1704.     // Write Sound data
  1705.     if ( !(output=fopen("SoundData", "w")) ) {
  1706.         Printwindow("Can't write to 'SoundData'", ERROR);
  1707.         return;
  1708.     }
  1709.     fwrite(sounddata, size[SOUND][version], numobj[SOUND][version], output);
  1710.     fclose(output);
  1711.  
  1712.     // Write Frame data
  1713.     if ( !(output=fopen("FrameData", "w")) ) {
  1714.         Printwindow("Can't write to 'FrameData'", ERROR);
  1715.         return;
  1716.     }
  1717.     fwrite(framedata, size[FRAME][version], numobj[FRAME][version], output);
  1718.     fclose(output);
  1719.  
  1720.     // Write Sprite data
  1721.     if ( !(output=fopen("SpriteData", "w")) ) {
  1722.         Printwindow("Can't write to 'SpriteData'", ERROR);
  1723.         return;
  1724.     }
  1725.     fwrite(spritedata, size[SPRITE][version], numobj[SPRITE][version], output);
  1726.     fclose(output);
  1727.  
  1728.     // Write Ammo data
  1729.     if ( !(output=fopen("AmmoData", "w")) ) {
  1730.         Printwindow("Can't write to 'AmmoData'", ERROR);
  1731.         return;
  1732.     }
  1733.     fwrite(maxammodata, size[AMMO][version], numobj[AMMO][version], output);
  1734.     fwrite(perammodata, size[AMMO][version], numobj[AMMO][version], output);
  1735.     fclose(output);
  1736.  
  1737.     // Write Weapon data
  1738.     if ( !(output=fopen("WeaponData", "w")) ) {
  1739.         Printwindow("Can't write to 'WeaponData'", ERROR);
  1740.         return;
  1741.     }
  1742.     fwrite(weapondata, size[WEAPON][version], numobj[WEAPON][version], output);
  1743.     fclose(output);
  1744.  
  1745.     // Write Text data
  1746.     if ( !(output=fopen("TextData", "w")) ) {
  1747.         Printwindow("Can't write to 'TextData'", ERROR);
  1748.         return;
  1749.     }
  1750.     fwrite(textdatap, size[TXT][version], 1, output);
  1751.     fclose(output);
  1752. }
  1753.  
  1754.